SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
3.6 KB · 68 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { notFound } from 'next/navigation';4import { AdminError, AdminHeader, fmtBytes } from '@/components/admin/ui';5import { KeyValues } from '@/components/meta/prose';6import { adminApi, AdminApiError } from '@/lib/admin-api';7import { fmtDateTime, fmtInt } from '@/lib/format';89export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {10  const { id } = await params;11  return { title: `Raw ${id}` };12}1314export default async function AdminRawRecordPage({ params }: { params: Promise<{ id: string }> }) {15  const { id } = await params;16  let record;17  try {18    record = (await adminApi.rawRecord(id)).data;19  } catch (e) {20    if (e instanceof AdminApiError && e.notFound) notFound();21    return (22      <>23        <AdminHeader title="Raw record" />24        <AdminError message={(e as Error).message} />25      </>26    );27  }28  return (29    <>30      <AdminHeader31        title={<span className="mono break-all">{record.source_native_id ?? record.id}</span>}32        lede={`${record.connector_name} · fetched ${fmtDateTime(record.fetched_at)}`}33        action={34          <Link href={`/admin/raw?connector=${encodeURIComponent(record.connector_name)}`} className="text-sm text-accent hover:underline">35            ← Raw records36          </Link>37        }38      />39      <KeyValues40        rows={[41          { k: 'Record id', v: <span className="mono text-xs">{record.id}</span> },42          { k: 'Run', v: record.run_id ? <Link href={`/admin/connectors/${encodeURIComponent(record.connector_name)}`} className="mono text-xs link">{record.run_id}</Link> : '—' },43          { k: 'Source', v: <span className="mono text-xs">{record.source_id}</span> },44          { k: 'Source URL', v: record.source_url ? <a href={record.source_url} rel="noopener noreferrer" target="_blank" className="link mono break-all text-xs">{record.source_url}</a> : '—' },45          { k: 'Storage path', v: <span className="mono break-all text-xs">{record.storage_path ?? '— (payload not kept: unchanged upstream)'}</span> },46          { k: 'Content type', v: <span className="mono text-xs">{record.content_type ?? '—'}</span> },47          { k: 'Size / records', v: `${fmtBytes(record.byte_size)} · ${fmtInt(record.record_count)} records` },48          { k: 'Payload hash', v: <span className="mono break-all text-xs">{record.payload_hash ?? '—'}</span> },49          { k: 'Processing', v: <span className={record.processing_status === 'processed' ? 'text-active' : record.processing_status === 'failed' ? 'text-danger' : ''}>{record.processing_status ?? '—'}{record.processed_at ? ` · ${fmtDateTime(record.processed_at)}` : ''}</span> },50          ...(record.error ? [{ k: 'Error', v: <span className="mono text-xs text-danger">{record.error}</span> }] : []),51        ]}52      />5354      <div className="mt-6 flex items-end justify-between gap-3">55        <h2 className="text-base font-semibold tracking-tight md:text-lg">Payload preview</h2>56        {record.truncated && <span className="rounded border border-warn/40 bg-warn-soft px-2 py-0.5 text-xs text-warn">truncated at 200 KB</span>}57      </div>58      {record.preview === null ? (59        <p className="mt-3 rounded-md border border-dashed border-rule-strong px-4 py-8 text-center text-sm text-ink-3">Payload file unavailable on this host (no storage path or file missing under SI_DATA_DIR/raw)</p>60      ) : (61        <pre className="scrollbar-thin mt-3 max-h-[70vh] overflow-auto rounded-md border border-rule bg-plane-2 p-3 text-[12px] leading-relaxed text-ink">62          <code className="mono">{record.preview}</code>63        </pre>64      )}65    </>66  );67}68